package com.gitee.wsl.mathematics.matrix.mat4

import com.gitee.wsl.mathematics.matrix.A
import com.gitee.wsl.mathematics.matrix.M
import com.gitee.wsl.mathematics.matrix.N
import com.gitee.wsl.mathematics.matrix.SquareMatrix
import com.gitee.wsl.mathematics.matrix.SquareMatrixD
import com.gitee.wsl.mathematics.matrix.SquareMatrixF
import com.gitee.wsl.mathematics.matrix.mat2.Matrix2
import com.gitee.wsl.mathematics.matrix.mat2.MutableMatrix2
import com.gitee.wsl.mathematics.matrix.mat3.Matrix3
import com.gitee.wsl.mathematics.matrix.mat3.MutableMatrix3
import com.gitee.wsl.mathematics.vector.vec3.Vector3
import com.gitee.wsl.mathematics.vector.vec4.MutableVector4
import com.gitee.wsl.mathematics.vector.vec4.Vector4

interface Matrix4<T:Number,V: Matrix4<T, V, R>,R:Vector4<T,R>>: SquareMatrix<T, V, R> {
    override val rowCount: Int get() = 4
    override val colCount: Int get() = 4

     val m00: T get() = proxy[0]
     val m01: T get() = proxy[1]
     val m02: T get() = proxy[2]
     val m03: T get() = proxy[3]
     val m10: T get() = proxy[4]
     val m11: T get() = proxy[5]
     val m12: T get() = proxy[6]
     val m13: T get() = proxy[7]
     val m20: T get() = proxy[8]
     val m21: T get() = proxy[9]
     val m22: T get() = proxy[10]
     val m23: T get() = proxy[11]
     val m30: T get() = proxy[12]
     val m31: T get() = proxy[13]
     val m32: T get() = proxy[14]
     val m33: T get() = proxy[15]

    override val m: List<Vector4<T, R>>

    override val n: List<Vector4<T, R>>

    val x get() = n[0]
    val y get() = n[1]
    val z get() = n[2]
    val w get() = n[3]

    fun createVec3(x:T,y:T,z:T): Vector3<T, *>

    fun createVec4(block:(MutableVector4<T, *>)->Unit): Vector4<T, *>

    fun createMat2(block:(MutableMatrix2<T, *, *>)->Unit): Matrix2<T, *, *>

    fun createMat3(block:(MutableMatrix3<T, *, *>)->Unit): Matrix3<T, *, *>

    //override fun createVec(indexRage: IntRange): Vector4<T, *>

    override fun toMutable(): MutableMatrix4<T, V, R>

    override fun createVec(indexes: List<Int>): Vector4<T, R>

    override operator fun M.get(row: Int) = createVec(rowIndexes(row))

    override operator fun N.get(col: Int) = createVec(colIndexes(col))

    operator fun A.get(row: Int, col: Int):Matrix3<T, *, *> = TODO()

    fun create(
        m00: T, m01: T, m02: T, m03: T,
        m10: T, m11: T, m12: T, m13: T,
        m20: T, m21: T, m22: T, m23: T,
        m30: T, m31: T, m32: T, m33: T
    ):V

    fun createNM(
        m00: T,m10: T,m20: T,m30: T,
        m01: T,m11: T,m21: T,m31: T,
        m02: T,m12: T,m22: T,m32: T,
        m03: T,m13: T,m23: T,m33: T
    ) = create(
        m00, m01, m02, m03,
        m10, m11, m12, m13,
        m20, m21, m22, m23,
        m30, m31, m32, m33
    )

    fun copy(
        m00: T?=null, m01: T?=null, m02: T?=null, m03: T?=null,
        m10: T?=null, m11: T?=null, m12: T?=null, m13: T?=null,
        m20: T?=null, m21: T?=null, m22: T?=null, m23: T?=null,
        m30: T?=null, m31: T?=null, m32: T?=null, m33: T?=null
    ) = create(
        m00 ?: this.m00,
        m01 ?: this.m01,
        m02 ?: this.m02,
        m03 ?: this.m03,
        m10 ?: this.m10,
        m11 ?: this.m11,
        m12 ?: this.m12,
        m13 ?: this.m13,
        m20 ?: this.m20,
        m21 ?: this.m21,
        m22 ?: this.m22,
        m23 ?: this.m23,
        m30 ?: this.m30,
        m31 ?: this.m31,
        m32 ?: this.m32,
        m33 ?: this.m33
    )

    /**
     * The trace of the 4x4 matrix
     */
    override val trace get() = m00 + m11 + m22 + m33

    val origin get() = createVec3(
                          this[0, 3],
                          this[1, 3],
                          this[2, 3]
                          )


    override val det:T get() {
        val a0 = m00 * m11 - m01 * m10
        val a1 = m00 * m12 - m02 * m10
        val a2 = m01 * m12 - m02 * m11
        val a3 = m20 * m31 - m21 * m30
        val a4 = m20 * m32 - m22 * m30
        val a5 = m21 * m32 - m22 * m31
        val a6 = m00 * a5 - m01 * a4 + m02 * a3
        val a7 = m10 * a5 - m11 * a4 + m12 * a3
        val a8 = m20 * a2 - m21 * a1 + m22 * a0
        val a9 = m30 * a2 - m31 * a1 + m32 * a0

        return m13 * a6 - m03 * a7 + m33 * a8 - m23 * a9
    }

    /** @return the determinant of the 3x3 upper left matrix */
    val det3x3: T
        get() =     m00 * m11 * m22 +
                    m01 * m12 * m20 +
                    m02 * m10 * m21 -
                    m00 * m12 * m21 -
                    m01 * m10 * m22 -
                    m02 * m11 * m20

    /**
     * The 3x3 top-left part of the 4x4 matrix
     */
    val matrix33
        get() = createMat3 { mat3->
            mat3.set(
                m00,m10,m20,
                m01,m11,m21,
                m02,m12,m22
            )
        }

    override fun times(other: V): V {
        fun f(x: Int, y: Int): T {
            return this[0, y] * other[x, 0] + this[1, y] * other[x, 1] + this[2, y] * other[x, 2] + this[3, y] * other[x, 3]
        }
        return create(
            f(0, 0), f(1, 0), f(2, 0),f(3, 0),
            f(0, 1), f(1, 1), f(2, 1),f(3, 1),
            f(0, 2), f(1, 2), f(2, 2),f(3, 2),
            f(0, 2), f(1, 2), f(2, 2),f(3, 3)
        )
    }
    /*create(
        m00 * other.m00 + m01 * other.m10 + m02 * other.m20 + m03 * other.m30,
        m00 * other.m01 + m01 * other.m11 + m02 * other.m21 + m03 * other.m31,
        m00 * other.m02 + m01 * other.m12 + m02 * other.m22 + m03 * other.m32,
        m00 * other.m03 + m01 * other.m13 + m02 * other.m23 + m03 * other.m33,

        m10 * other.m00 + m11 * other.m10 + m12 * other.m20 + m13 * other.m30,
        m10 * other.m01 + m11 * other.m11 + m12 * other.m21 + m13 * other.m31,
        m10 * other.m02 + m11 * other.m12 + m12 * other.m22 + m13 * other.m32,
        m10 * other.m03 + m11 * other.m13 + m12 * other.m23 + m13 * other.m33,

        m20 * other.m00 + m21 * other.m10 + m22 * other.m20 + m23 * other.m30,
        m20 * other.m01 + m21 * other.m11 + m22 * other.m21 + m23 * other.m31,
        m20 * other.m02 + m21 * other.m12 + m22 * other.m22 + m23 * other.m32,
        m20 * other.m03 + m21 * other.m13 + m22 * other.m23 + m23 * other.m33,

        m30 * other.m00 + m31 * other.m10 + m32 * other.m20 + m33 * other.m30,
        m30 * other.m01 + m31 * other.m11 + m32 * other.m21 + m33 * other.m31,
        m30 * other.m02 + m31 * other.m12 + m32 * other.m22 + m33 * other.m32,
        m30 * other.m03 + m31 * other.m13 + m32 * other.m23 + m33 * other.m33
    )*/

    override val adjoin: V
        get() {
            val a00 = m00 * m11 - m01 * m10
            val a01 = m00 * m12 - m02 * m10
            val a02 = m00 * m13 - m03 * m10
            val a03 = m01 * m12 - m02 * m11
            val a04 = m01 * m13 - m03 * m11
            val a05 = m02 * m13 - m03 * m12
            val a06 = m20 * m31 - m21 * m30
            val a07 = m20 * m32 - m22 * m30
            val a08 = m20 * m33 - m23 * m30
            val a09 = m21 * m32 - m22 * m31
            val a10 = m21 * m33 - m23 * m31
            val a11 = m22 * m33 - m23 * m32

            // Calculate the determinant
            return create(
                (m11 * a11 - m12 * a10 + m13 * a09),
                (m02 * a10 - m01 * a11 - m03 * a09),
                (m31 * a05 - m32 * a04 + m33 * a03),
                (m22 * a04 - m21 * a05 - m23 * a03),
                (m12 * a08 - m10 * a11 - m13 * a07),
                (m00 * a11 - m02 * a08 + m03 * a07),
                (m32 * a02 - m30 * a05 - m33 * a01),
                (m20 * a05 - m22 * a02 + m23 * a01),
                (m10 * a10 - m11 * a08 + m13 * a06),
                (m01 * a08 - m00 * a10 - m03 * a06),
                (m30 * a04 - m31 * a02 + m33 * a00),
                (m21 * a02 - m20 * a04 - m23 * a00),
                (m11 * a07 - m10 * a09 - m12 * a06),
                (m00 * a09 - m01 * a07 + m02 * a06),
                (m31 * a01 - m30 * a03 - m32 * a00),
                (m20 * a03 - m21 * a01 + m22 * a00)
            )
        }

    /**
     * Inversed version of the 4x4 matrix
     */
    val inversed :V
        get() {
            /*if (this === IDENTITY) {
                return this
            }*/
            val n00 = m21 * m32 * m13 - m31 * m22 * m13 + m31 * m12 * m23 - m11 * m32 * m23 - m21 * m12 * m33 + m11 * m22 * m33
            val n01 = m30 * m22 * m13 - m20 * m32 * m13 - m30 * m12 * m23 + m10 * m32 * m23 + m20 * m12 * m33 - m10 * m22 * m33
            val n02 = m20 * m31 * m13 - m30 * m21 * m13 + m30 * m11 * m23 - m10 * m31 * m23 - m20 * m11 * m33 + m10 * m21 * m33
            val n03 = m30 * m21 * m12 - m20 * m31 * m12 - m30 * m11 * m22 + m10 * m31 * m22 + m20 * m11 * m32 - m10 * m21 * m32
            val n10 = m31 * m22 * m03 - m21 * m32 * m03 - m31 * m02 * m23 + m01 * m32 * m23 + m21 * m02 * m33 - m01 * m22 * m33
            val n11 = m20 * m32 * m03 - m30 * m22 * m03 + m30 * m02 * m23 - m00 * m32 * m23 - m20 * m02 * m33 + m00 * m22 * m33
            val n12 = m30 * m21 * m03 - m20 * m31 * m03 - m30 * m01 * m23 + m00 * m31 * m23 + m20 * m01 * m33 - m00 * m21 * m33
            val n13 = m20 * m31 * m02 - m30 * m21 * m02 + m30 * m01 * m22 - m00 * m31 * m22 - m20 * m01 * m32 + m00 * m21 * m32
            val n20 = m11 * m32 * m03 - m31 * m12 * m03 + m31 * m02 * m13 - m01 * m32 * m13 - m11 * m02 * m33 + m01 * m12 * m33
            val n21 = m30 * m12 * m03 - m10 * m32 * m03 - m30 * m02 * m13 + m00 * m32 * m13 + m10 * m02 * m33 - m00 * m12 * m33
            val n22 = m10 * m31 * m03 - m30 * m11 * m03 + m30 * m01 * m13 - m00 * m31 * m13 - m10 * m01 * m33 + m00 * m11 * m33
            val n23 = m30 * m11 * m02 - m10 * m31 * m02 - m30 * m01 * m12 + m00 * m31 * m12 + m10 * m01 * m32 - m00 * m11 * m32
            val n30 = m21 * m12 * m03 - m11 * m22 * m03 - m21 * m02 * m13 + m01 * m22 * m13 + m11 * m02 * m23 - m01 * m12 * m23
            val n31 = m10 * m22 * m03 - m20 * m12 * m03 + m20 * m02 * m13 - m00 * m22 * m13 - m10 * m02 * m23 + m00 * m12 * m23
            val n32 = m20 * m11 * m03 - m10 * m21 * m03 - m20 * m01 * m13 + m00 * m21 * m13 + m10 * m01 * m23 - m00 * m11 * m23
            val n33 = m10 * m21 * m02 - m20 * m11 * m02 + m20 * m01 * m12 - m00 * m21 * m12 - m10 * m01 * m22 + m00 * m11 * m22

            val d = det
            return create(
                n00 / d, n10 / d, n20 / d, n30 / d,
                n01 / d, n11 / d, n21 / d, n31 / d,
                n02 / d, n12 / d, n22 / d, n32 / d,
                n03 / d, n13 / d, n23 / d, n33 / d
            )
        }

    /**
     * Returns a transposed version of the matrix
     */
    val transposed: V
        get() = create(
            m00, m10, m20, m30,
            m01, m11, m21, m31,
            m02, m12, m22, m32,
            m03, m13, m23, m33
        )

    val normal:V
        get() {
            val subView = create(
                m00, m10, m20, zero,
                m01, m11, m21, zero,
                m02, m12, m22, zero,
                zero, zero, zero, one)

            return subView.inversed.transposed.copy(m33 = zero)
        }
}

interface Matrix4F<V: Matrix4<Float, V, R>, R:Vector4<Float,R>>:  Matrix4<Float, V, R>, SquareMatrixF<V, R>

interface Matrix4D<V: Matrix4<Double, V, R>,R:Vector4<Double,R>>: Matrix4<Double, V, R>, SquareMatrixD<V, R>